my $accounts = fetchall_hash("accounts");

# normalise ssh keys into a hashref
foreach my $info (values %{ $accounts })
{
    $info->{keys} = { map +($_ => 1), comma_or_arrayref($info->{keys}) }
	unless ref $info->{keys} eq 'HASH';
}

foreach my $keys (fetch_all("keys"))
{
    my @keys = ref $keys eq 'HASH' ? %$keys : ("root", $keys);

    while (@keys)
    {
	my ($user, $keys) = splice(@keys, 0, 2);

	$accounts->{$user}{keys}{$_}++
	    foreach comma_or_arrayref($keys);
    }
}

my @ssh_keys = grep !/^\s*(#|$)/, split /\n/, slurp_file("$ROLLOUT/conf/authorized_keys");
my @ssh2_keys = grep !/^\s*(#|$)/, split /\n/, slurp_file("$ROLLOUT/conf/authorized_keys2");

while (my ($user, $info) = each %{ $accounts })
{
    my ($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell,$expire) = getpwnam($user);

    if (my $group = $info->{gid})
    {
	if ($group =~ /^\d+$/)
	{
	    unless (getgrgid($group))
	    {
		command("groupadd", "-g", $group, $user);
		getgrgid($group) or die "Couldn't create group: $!";
	    }
	}
	else
	{
	    unless ($gid = getgrnam($group))
	    {
		command("groupadd", $group);
		$gid = getgrnam($group) or die "Couldn't create group: $!";
	    }

	    $info->{gid} = $gid;
	}
    }

    my $create = 1;
    $create = 0 if defined $uid;

    my @args;
    push @args, "-u", $info->{uid} if $info->{uid} and $info->{uid} ne $uid;
    push @args, "-g", $info->{gid} if $info->{gid} and $info->{gid} ne $gid;
    push @args, "-s", $info->{shell} if $info->{shell} and $info->{shell} ne $shell;
    push @args, "-d", $info->{home} if $info->{home} and $info->{home} ne $dir;
    push @args, "-c", $info->{comment} if $info->{comment} and $info->{comment} ne $comment;
    push @args, "-G", $info->{groups} if $info->{groups};

    # find actual password from shadow file
    # to determine if the account needs to be unlocked
    unless ($create)
    {
	($passwd) =
	    map /^\Q$user\E:([^:]*):/,
	    split /\n/,
	    slurp_file("/etc/shadow");
    }

    my $set_passwd =
	$info->{lock} ? '!!' :
	$create ? $info->{passwd} || '*' :
	$passwd eq '!!' ? '*' : $passwd;

    push @args, "-p", $set_passwd
	if $create or $set_passwd ne $passwd;

    if ($create)
    {
	my $skel = exists $info->{skel} ? $info->{skel} : '/etc/skel';

	if ($skel)
	{
	    push @args, "-m", "-k", $skel;
	}
	else
	{
	    # if skel is '', just create the directory
	    push @args, "-M";
	    dir_check($info->{home}, $info);
	}
    }

    my $command = $create ? "useradd" : "usermod";

    command($command, @args, $user)
	if @args;

    my $home = (getpwnam $user)[7] or die "No home directory for $user\n";
    my $labels = join("|", keys %{ $info->{keys} });
    my $ssh_keys = join("", map "$_\n", grep /\s(?:$labels)$/, @ssh_keys);
    my $ssh2_keys = join("", map "$_\n", grep /\s(?:$labels)$/, @ssh2_keys);

    dir_check("$home/.ssh", { mode=>0755 });
    text_install("$home/.ssh/authorized_keys", $ssh_keys);
    text_install("$home/.ssh/authorized_keys2", $ssh2_keys);
}

1;
